package com.toobigtofail
{
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2ContactListener;
	
	import com.toobigtofail.entities.BuildingBlock;
	import com.toobigtofail.entities.Enemy;
	import com.toobigtofail.entities.Obstacle;
	import com.toobigtofail.entities.Target;
	import com.toobigtofail.palette.Tray;
	import com.toobigtofail.placeholders.Placeholder;
	import com.toobigtofail.shapes.Ball;
	import com.toobigtofail.shapes.Box;
	import com.toobigtofail.shapes.Line;
	import com.toobigtofail.shapes.Octagon;
	import com.toobigtofail.utility.ContactListener;
	import com.toobigtofail.utility.Level;
	import com.toobigtofail.utility.LevelParser;
	import com.toobigtofail.utility.LineDrawer;
	import com.toobigtofail.utility.Util;
	
	import fabulaexmachina.box2fp.Box2DEntity;
	import fabulaexmachina.box2fp.Box2DWorld;
	
	import net.flashpunk.Entity;
	import net.flashpunk.FP;
	import net.flashpunk.graphics.Text;
	import net.flashpunk.utils.Draw;
	import net.flashpunk.utils.Input;
	import net.flashpunk.utils.Key;
	
	public class Game extends Box2DWorld
	{
		[Embed(source = "/assets/levels/levels.xml", mimeType = "application/octet-stream")] private static const LEVEL_XML:Class;
		
		private var _placeholder:Placeholder;
		private var _state:String = "building";
		private var _tray:Tray;
		private var _levelParser:LevelParser;
		private var _endText:Entity = new Entity(200,200);
		
		// Variables for capturing line drawing
		private var _lineDrawer:LineDrawer = new LineDrawer(0);
		
		public function Game() {
			_levelParser = new LevelParser(LEVEL_XML);
						
			super();
		}
		
		private function clear():void {
			remove(_endText);
			_placeholder = null;
			_tray.remove();
			if (_lineDrawer.world) {
				remove(_lineDrawer);
			}
			_lineDrawer.reset();
			_lineDrawer.linePower = 0;
			
			var entities:Array = new Array;
			getClass(Enemy, entities);
			getClass(Placeholder, entities);
			getClass(Target, entities);
			getClass(BuildingBlock, entities);
			getClass(Obstacle, entities);
						
			for each (var e:Entity in entities) {
				if (e is Box2DEntity) {
					(e as Box2DEntity).remove();
				}
				else {
					remove(e);
				}
			}			
		}
		
		private function startLevel(level:Level):void {
			_tray = new Tray;
			add(_tray);
			
			_lineDrawer.linePower = level.drawPower;
			_lineDrawer.setDrawArea(level.buildAreaTopLeft, level.buildAreaWidth, level.buildAreaHeight);
			
			var e:Entity;
			for each (e in level.enemies) {
				add(e);
			}
			for each (e in level.targets) {
				add(e);
			}
			for each (e in level.obstacles) {
				add(e);
			}
			for each (e in level.placeholders) {
				_tray.addItem(e as Placeholder);
			}

			_state = "building";
		}
		
		private function nextLevel():void {
			clear();
			var level:Level = _levelParser.nextLevel;
			if (null != level) {
				startLevel(level);
			}
		}
		
		private function restart():void {
			clear();
			startLevel(_levelParser.resetLevel());
		}
		
		override public function begin():void {
			setGravity(new b2Vec2 (0.0, 10.0));
			
			// Outer walls
			add(new Box(FP.screen.width/2, FP.screen.height, FP.screen.width/26, 1.0/30));
			add(new Box(FP.screen.width/2, 0, FP.screen.width/26, 1.0/30));
			add(new Box(0, FP.screen.height/2, 1/30, FP.screen.height/26));
			add(new Box(FP.screen.width, FP.screen.height/2, 1/30, FP.screen.height/26, 0, 0, 0, 0, 1, 1, 0.3));
			
			startLevel(_levelParser.currentLevel);
			
			this.unpause();
			doDebug();
			
			this.b2world.SetContactListener(new ContactListener);
		}
		
		override public function update():void {
			super.update();
			var entities:Array;
			var e:Box2DEntity;
			
			if (Input.pressed(Key.R)) {
				restart();
				return;
			}
			
			switch(_state) {
				case "drawing":
					if (_lineDrawer.isDone) {
						var line:Line = _lineDrawer.finalizeLine();
						if (null != line) {
							add(new Obstacle(line));
						}
						remove(_lineDrawer);
						_state = "building";
					}
					
					break;
				case "building":
					if (_placeholder) {
						_placeholder.centerOnMouse(Input.mouseX,Input.mouseY);
					}
					
					if (Input.mousePressed) {
						if (_placeholder && inBuildArea(new b2Vec2(Input.mouseX, Input.mouseY))) {
							
							add(_placeholder.getBox2DEntity());
							remove(_placeholder);
							_placeholder = null;
						}
						else if (_tray.click(Input.mouseX, Input.mouseY)) {
							if (_tray.hasSelectedItem()) {
								_placeholder = _tray.selectedItem;
								add(_placeholder);
							}
						}
					}
					else if (Input.released(Key.G)) {
						_tray.remove();
						_state = "playing";
						entities = new Array;
						getClass(Enemy,entities);
						for each (var enemy:Enemy in entities) {
							enemy.go();						
						}
					}
					else if (!_placeholder && Input.released(Key.D)) {
						draw();
					}					
					
					break;
				case "playing":
					var isAllStill:Boolean = true;
					entities = new Array;
					getType(BuildingBlock.TYPE,entities);
					getClass(Enemy,entities);
					for each (e in entities) {
						if (e.body.IsAwake()) {
							isAllStill = false;
							break;
						}
					}
					if (isAllStill) {
						_state = "calculate";
					}
					break;
				case "calculate":
					entities = new Array;
					getType("target",entities);
					if (entities.length) {
						if (_levelParser.hasNextLevel()) {
							_endText.graphic = new Text("YOU WIN! (press R to restart) (press N for the next level)");
							_state = "level_win";
						}
						else {
							_endText.graphic = new Text("YOU WIN! That was the last level! (press R to restart this level)");
							_state = "game_over";
						}
					}
					else {
						_endText.graphic = new Text("YOU LOSE! (press R to restart)");
						_state = "level_lose";
					}					
					add(_endText);
					
					break;
				case "level_win":
					if (Input.pressed(Key.N)) {
						remove(_endText);
						nextLevel();
						_state = "building";
					}
					break;
			}
			
		}
		
		override public function render():void {
			super.render();
			
			var lvl:Level = _levelParser.currentLevel;
			Draw.rect(lvl.buildAreaTopLeft.x, lvl.buildAreaTopLeft.y, lvl.buildAreaWidth, lvl.buildAreaHeight, 0x00FF00, 0.1);
		}
		
		private function inBuildArea(p:b2Vec2):Boolean {
			var lvl:Level = _levelParser.currentLevel;
			return Util.inRect(p, lvl.buildAreaTopLeft, lvl.buildAreaWidth, lvl.buildAreaHeight);
		}
		
		private function draw():void {
			_lineDrawer.reset();
			add(_lineDrawer);
			
			_state = "drawing";
		}
	}
}